﻿/*
题目: 地下城游戏

恶魔们抓住了公主并将她关在了地下城 dungeon 的 右下角 。地下城是由 m x n 个房间组成的二维网格。我们英勇的骑士最初被安置在 左上角 的房间里，他必须穿过地下城并通过对抗恶魔来拯救公主。

骑士的初始健康点数为一个正整数。如果他的健康点数在某一时刻降至 0 或以下，他会立即死亡。

有些房间由恶魔守卫，因此骑士在进入这些房间时会失去健康点数（若房间里的值为负整数，则表示骑士将损失健康点数）；其他房间要么是空的（房间里的值为 0），要么包含增加骑士健康点数的魔法球（若房间里的值为正整数，则表示骑士将增加健康点数）。

为了尽快解救公主，骑士决定每次只 向右 或 向下 移动一步。

返回确保骑士能够拯救到公主所需的最低初始健康点数。

注意：任何房间都可能对骑士的健康点数造成威胁，也可能增加骑士的健康点数，包括骑士进入的左上角房间以及公主被监禁的右下角房间。

https://leetcode.cn/problems/dungeon-game/description/
*/

#include <iostream>
#include <random>
#include <string>
#include <vector>
#include <list>
#include "TreeNode.hpp"
#include "ListNode.hpp"
#include <map>
#include <set>
#include <stack>
#include <queue>
#include <unordered_map>
#include <unordered_set>
#include <algorithm>
#include <functional>

using namespace std;

class Solution {
public:
    /**
    * 如果从 (0, 0) dp 到 (n - 1, m - 1),
    * 那么既需要考虑 当前最大血量，又要考虑 初始所需最少血量
    * 甚至有时候 路径会发生切换
    * 所以考虑从 (n - 1, m - 1) dp 到 (0, 0)
    * 并且定义为 初始所需的最少血量
    * 那么这时候反着走，遇到扣血的，就会提高 最初血量。
    * 如果遇到加血的，就会降低 最初血量
    */
    int calculateMinimumHP(vector<vector<int>>& dungeon) {
        int n = dungeon.size();
        int m = dungeon[0].size();

        vector<vector<int>> dp(n + 1, vector<int>(m + 1, INT_MAX));
        // 比如 -6 ，那么至少为 7；如果 2，那么至少也为 1，完成了统一
        // dp[n - 1][m - 1] = max(1, 1 - dungeon[n - 1][m - 1]);
        dp[n][m - 1] = dp[n - 1][m] = 1;    // 至少有 1 滴血

        for (int i = n - 1; i >= 0; i--) {
            for (int j = m - 1; j >= 0; j--) {
                dp[i][j] = max(min(dp[i + 1][j], dp[i][j + 1]) - dungeon[i][j], 1);
            }
        }

        return dp[0][0];
    }
};
